#include "cdev.h"

static int cdev_open(struct inode *inode, struct file *filep);
static int cdev_release(struct inode *inode, struct file *filep);
static ssize_t cdev_read(struct file *filep, char __user *buffer, size_t size, loff_t *pos);
static ssize_t cdev_write(struct file *filep, const char __user *buffer, size_t size, loff_t *pos);

//设备号
static dev_t dev;
//cdev 对象，用于管理字符设备
static struct cdev char_dev;
//字符设备操作方法
static struct file_operations ops = 
{
	.owner = THIS_MODULE,
	.open = cdev_open,
	.release = cdev_release,
	.read = cdev_read,
	.write = cdev_write,
};

static int cdev_open(struct inode *inode, struct file *filep)
{
	dev_t rdev;

	//inode节点的i_rdev即是当前设备的设备号
	rdev = inode->i_rdev;
	//file对象的private_data是专为驱动预留的一个成员
	filep->private_data = (void*)rdev;
	printk("%s\r\n", __FUNCTION__);
	printk("major = %d,", MAJOR(rdev));
	printk("minor = %d\r\n", MINOR(rdev));
	return 0;
}

static int cdev_release(struct inode *inode, struct file *filep)
{
	dev_t rdev = (dev_t)filep->private_data;

	printk("%s\r\n", __FUNCTION__);
	printk("major = %d,", MAJOR(rdev));
	printk("minor = %d\r\n", MINOR(rdev));
	return 0;
}

static ssize_t cdev_read(struct file *filep, char __user *buffer, size_t size, loff_t *pos)
{
	dev_t rdev = (dev_t)filep->private_data;

	printk("%s\r\n", __FUNCTION__);
	printk("major = %d,", MAJOR(rdev));
	printk("minor = %d\r\n", MINOR(rdev));
	return -1;
}

static ssize_t cdev_write(struct file *filep, const char __user *buffer, size_t size, loff_t *pos)
{
	dev_t rdev = (dev_t)filep->private_data;

	printk("%s\r\n", __FUNCTION__);
	printk("major = %d,", MAJOR(rdev));
	printk("minor = %d\r\n", MINOR(rdev));
	return -1;
}

static int __init char_init(void)
{
	int i;
	int ret;

	printk("cdev init\r\n");
#ifndef USING_AUTO_REGION
	/* 合成设备号
	 * ma 主设备号
	 * mi 次设备号
	 * 返回合成的设备号
	 */
	dev = MKDEV(CDEV_MAJOR, CDEV_MINOR);
	/* 注册一系列字符设备号
	 * from 起始设备号
	 * count 要注册的数量，注册多个时设备号依次递增
	 * name 标记主设备号名称
	 * 成功返回0
	 */
	ret = register_chrdev_region(dev, CDEV_CNT, CDEV_NAME);
	if(ret != 0)
	{
		printk("region device ID failed\r\n");
		return ret;
	}
	for(i=0; i<CDEV_CNT; i++)
	{
		printk("device%d ", i);
		/* 提取主设备号
		 * dev 设备号
		 * 返回主设备号
		 */
		printk("major = %d,", MAJOR(dev+i));
		/* 提取次设备号
		 * dev 设备号
		 * 返回次设备号
		 */
		printk("minor = %d\r\n", MINOR(dev+i));
	}
#else
	/* 动态分配一系列字符设备号
	 * dev 用于返回第一个设备号
	 * baseminor 次设备号起始值
	 * count 要分配的数量，注册多个时设备号依次递增
	 * name 标记主设备号名称
	 * 成功返回0
	 */
	ret = alloc_chrdev_region(&dev, CDEV_MINOR, CDEV_CNT, CDEV_NAME);
	if(ret != 0)
	{
		printk("alloc device ID failed\r\n");
		return ret;
	}
	for(i=0; i<CDEV_CNT; i++)
	{
		printk("device%d ", i);
		printk("major = %d,", MAJOR(dev+i));
		printk("minor = %d\r\n", MINOR(dev+i));
	}
#endif
	/* 初始化 cdev 对象
	 * cdev 要初始化的对象
	 * fops 设备操作函数
	 */
	cdev_init(&char_dev, &ops);
	char_dev.owner = THIS_MODULE;
	/* 添加cdev对象到内核
	 * p 指向要添加的cdev对象
	 * dev 设备号
	 * count cdev对象所管理的设备数量
	 */
	ret = cdev_add(&char_dev, dev, CDEV_CNT);
	if(ret != 0)
	{
		unregister_chrdev_region(dev, CDEV_CNT);
		printk("add cdev failed\r\n");
		return ret;
	}
	return 0;
}

static void __exit char_exit(void)
{
	printk("cdev exit\r\n");
	/* 从内核删除cdev对象
	 * p 要删除的内核对象
	 */
	cdev_del(&char_dev);
	/* 注销一系列字符设备号
	 * from 起始设备号
	 * count 要注销的数量
	 */
	unregister_chrdev_region(dev, CDEV_CNT);
}


module_init(char_init);
module_exit(char_exit);

MODULE_LICENSE("GPL");
